home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume20 / mtalk / part01 next >
Encoding:
Text File  |  1991-06-08  |  27.8 KB  |  958 lines

  1. Newsgroups: comp.sources.misc
  2. From: Mathew Kimmel <kimmel@umvlsi.ecs.umass.edu>
  3. Subject:  v20i044:  mtalk - simple multi-user chat for Coherent, Part01/01
  4. Message-ID: <1991Jun7.181838.12644@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: 77a29ba4e00e5e25f4c5f0d02ac1de0f
  6. Date: Fri, 7 Jun 1991 18:18:38 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: Mathew Kimmel <kimmel@umvlsi.ecs.umass.edu>
  10. Posting-number: Volume 20, Issue 44
  11. Archive-name: mtalk/part01
  12. Environment: Coherent
  13.  
  14. This is a very small and simple multi-user chat program I wrote for
  15. Coherent.  I wrote it as an exercise to teach myself about interprocess
  16. communications, but thought it might be useful to the world as a whole.
  17. It takes up very little memory, and doesn't monopolize CPU time.  It can
  18. handle as many as 30 users (or more?); the maximum number is set at
  19. compile time.  It may also compile on systems other than Coherent--I
  20. haven't tried any others, but the code is fairly generic.
  21.  
  22. -Matt
  23. ---cut here---
  24. #! /bin/sh
  25. # This is a shell archive.  Remove anything before this line, then unpack
  26. # it by saving it into a file and typing "sh file".  To overwrite existing
  27. # files, type "sh file -c".  You can also feed this as standard input via
  28. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  29. # will see the following message at the end:
  30. #        "End of shell archive."
  31. # Contents:  Makefile README ftok.c mtalk.c mtalk.h mtalk.man mtalkd.c
  32. #   mtalkd.man
  33. # Wrapped by kimmel@umvlsi.ecs.umass.edu on Fri Jun  7 01:46:02 1991
  34. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  35. if test -f Makefile -a "${1}" != "-c" ; then 
  36.   echo shar: Will not over-write existing file \"Makefile\"
  37. else
  38. echo shar: Extracting \"Makefile\" \(229 characters\)
  39. sed "s/^X//" >Makefile <<'END_OF_Makefile'
  40. XCFLAGS = -O
  41. X
  42. Xall: mtalk mtalkd
  43. X
  44. Xmtalk: mtalk.o ftok.o
  45. X  cc $(CFLAGS) -o mtalk mtalk.o ftok.o
  46. X
  47. Xmtalkd: mtalkd.o ftok.o
  48. X  cc $(CFLAGS) -o mtalkd mtalkd.o ftok.o
  49. X
  50. Xmtalk.o: mtalk.c mtalk.h
  51. X
  52. Xmtalkd.o: mtalkd.c mtalk.h
  53. X
  54. Xftok.o: ftok.c
  55. END_OF_Makefile
  56. if test 229 -ne `wc -c <Makefile`; then
  57.     echo shar: \"Makefile\" unpacked with wrong size!
  58. fi
  59. # end of overwriting check
  60. fi
  61. if test -f README -a "${1}" != "-c" ; then 
  62.   echo shar: Will not over-write existing file \"README\"
  63. else
  64. echo shar: Extracting \"README\" \(4066 characters\)
  65. sed "s/^X//" >README <<'END_OF_README'
  66. XINTRODUCTION
  67. X
  68. Xmtalk is a small, simple multi-user chat program.  It consists of a
  69. Xdaemon, mtalkd, which sits in memory and receives and sends data using
  70. Xkernel message passing to user processes which have invoked the client
  71. Xprogram and user interface, mtalk.  As the code stands at this release,
  72. Xthe daemon takes up only 15K or so of memory, as does each user process.
  73. XI've tested it with three simultaneous users, and it produced no significant
  74. Xdrop in processing speed on my machine (a 286 laptop).
  75. X
  76. X
  77. XMAKING mtalk
  78. X
  79. XBefore running make and compiling the programs, you must edit the header
  80. Xfile mtalk.h to reflect your system's configuration.  Of special importance
  81. Xare the #defines DAEMON_PATH and CLIENT_PATH.  These must contain the
  82. Xfull pathnames of the daemon and client executables once they are compiled.
  83. XIf either program can't find these two files when you run it, it will
  84. Xcrash immediately.  Other defines you may want to change are MAX_USERS,
  85. Xthe maximum number of users allowed on the system at any given time (I
  86. Xwould not recommend setting this above 30, but who has 30 users at a time
  87. Xon Coherent anyway?) and MAX_MESSAGE, which is the maximum length of a
  88. Xpublic message.  Care should be taken in changing MAX_MESSAGE, because
  89. Xif it is too big it may exceed Coherent's maximum message size and crash
  90. Xthe system.
  91. X
  92. X
  93. XINSTALLING AND RUNNING mtalk
  94. X
  95. XAssuming you have successfully compiled mtalk and mtalkd and put them in
  96. Xthe directories specified in DAEMON_PATH and CLIENT_PATH, you have two
  97. Xoptions for installing the mtalk daemon: installing it for one session,
  98. Xor installing it permanently.  Before you do either, however, I recommend
  99. Xthat you set mtalkd's owner to root and chmod it to 500 (read and execute
  100. Xfor owner only) to prevent users from running duplicate copies (which would
  101. Xreally mess things up).  mtalk, of course, should be chmod'd to 755, so
  102. Xeveryone can execute it.  That done, here are the two methods of installation:
  103. X
  104. X1) For one session only.  mtalk uses the inter-process message passing
  105. X   system calls, which are implemented under Coherent as a device driver.
  106. X   Therefore you, as the superuser, need to load the device driver and
  107. X   then execute the daemon as a background process.  To do this, log in
  108. X   as root and type:
  109. X
  110. X     /etc/drvld -r /drv/msg
  111. X     mtalkd&
  112. X
  113. X   This will start the daemon as a background process, which will stay
  114. X   in the system until you kill it or shut the system down.  Although
  115. X   it will still technically be associated with the console (or whatever
  116. X   device you were using when you executed it); however, mtalkd takes
  117. X   steps to dissociate itself from its terminal and will not shut down
  118. X   if you log out or type the interrupt character.  It can only be killed
  119. X   with the kill command, by the SIGTERM signal (i.e. just kill <pid> to
  120. X   kill it).  NEVER kill mtalkd with a SIGQUIT signal, because it will
  121. X   be forced to exit without cleaning up message queues and will make a
  122. X   mess of the message system.
  123. X
  124. X2) Permanently.  To install mtalkd permanently, so that it is automatically
  125. X   loaded in the background whenever you boot Coherent, you must change
  126. X   two files in the etc directory (as root).  First, add the following line
  127. X   to the file drvld.all:
  128. X
  129. X     /etc/drvld -r /drv/msg
  130. X
  131. X   Then, add a line to rc, in the section that invokes the cron and update
  132. X   daemons, invoking mtalkd as a daemon (i.e. executing it in the back-
  133. X   ground).  Then shut down and reboot the system.
  134. X
  135. XOnce you've done either of these, users can use mtalk by executing the
  136. Xmtalk program.  See the mtalk man page for details about commands.
  137. X
  138. X
  139. XNOTES
  140. X
  141. XI wrote this program as an exercise to learn to use the message passing
  142. Xfunctions.  When I was done, I saw that I had a small and simple chat
  143. Xprogram that others might find useful.  The code is well commented and
  144. Xshould be easily extensible; feel free to make modifications and
  145. Xdistribute them; just be sure and let me know (and send me a copy!).
  146. XIf anyone has comments, questions, or bug reports, please e-mail me at
  147. Xkimmel@umvlsi.ecs.umass.edu
  148. X
  149. XEnjoy!
  150. X
  151. X-Matt
  152. END_OF_README
  153. if test 4066 -ne `wc -c <README`; then
  154.     echo shar: \"README\" unpacked with wrong size!
  155. fi
  156. # end of overwriting check
  157. fi
  158. if test -f ftok.c -a "${1}" != "-c" ; then 
  159.   echo shar: Will not over-write existing file \"ftok.c\"
  160. else
  161. echo shar: Extracting \"ftok.c\" \(1216 characters\)
  162. sed "s/^X//" >ftok.c <<'END_OF_ftok.c'
  163. X/* key_t ftok(filename,c) - Create a unique IPC key based on a filename
  164. X *                          and an 8-bit number.
  165. X *
  166. X * This function takes as parameters a pointer to an ascii string
  167. X * containing the pathname of a file, and an integer.  It then returns
  168. X * a (hopefully) unique IPC key.  The key is a 32-bit integer, and is
  169. X * constructed as follows: the lower 8 bits are the low 8 bits of c.
  170. X * The next 8 bits are the low 8 bits of the device descriptor of the
  171. X * device the file is located on.  The upper 16 bits are the inode
  172. X * of the file.
  173. X *
  174. X * This code copyright (c) Matt Kimmel 1991.  Permission granted for
  175. X * unrestricted use in non-commercial products.
  176. X */
  177. X#include <sys/ipc.h>
  178. X#include <sys/stat.h>
  179. X
  180. Xkey_t ftok(filename,c)
  181. Xchar *filename;
  182. Xint c;
  183. X{
  184. X  struct stat fs;
  185. X  union {
  186. X    key_t key;
  187. X    struct {
  188. X      char c;
  189. X      char dev;
  190. X      int inode;
  191. X      } info;
  192. X    } keyval;
  193. X
  194. X  /* First attempt to stat the file */
  195. X  if(stat(filename,&fs) == -1) {
  196. X    perror("ftok");
  197. X    exit(1); /* Best to exit if this happens, or we may have a major IPC collision... */
  198. X    }
  199. X
  200. X  keyval.info.c = (char)c;
  201. X  keyval.info.dev = (char)fs.st_dev;
  202. X  keyval.info.inode = (int)fs.st_ino;
  203. X  return(keyval.key);
  204. X}
  205. X
  206. END_OF_ftok.c
  207. if test 1216 -ne `wc -c <ftok.c`; then
  208.     echo shar: \"ftok.c\" unpacked with wrong size!
  209. fi
  210. # end of overwriting check
  211. fi
  212. if test -f mtalk.c -a "${1}" != "-c" ; then 
  213.   echo shar: Will not over-write existing file \"mtalk.c\"
  214. else
  215. echo shar: Extracting \"mtalk.c\" \(7324 characters\)
  216. sed "s/^X//" >mtalk.c <<'END_OF_mtalk.c'
  217. X/* Client for mtalk - multiuser talk program.  This program is invoked
  218. X * by a user and sends and receives messages from the mtalk daemon.
  219. X *
  220. X * Copyright (c) 1991, Matthew Kimmel.  Permission granted for unlimted
  221. X * non-commercial use and distribution.
  222. X */
  223. X#include <stdio.h>
  224. X#include <string.h>
  225. X#include <ctype.h>
  226. X#include <signal.h>
  227. X#include <errno.h>
  228. X#include <sgtty.h>
  229. X#include <sys/fcntl.h>
  230. X#include <sys/msg.h>
  231. X#include "mtalk.h"
  232. X
  233. Xstruct genmsg msgin;  /* Buffer for unprocessed messages */
  234. Xint ouruid; /* uid of this user */
  235. Xint ourmqid; /* our message queue id */
  236. Xint dmqid; /* mtalk daemon's message queue id */
  237. Xstruct sgttyb t0, t1; /* tty structs for stdin and stdout */
  238. Xint oldfcntl; /* preserved old fcntl value for stdin */
  239. X
  240. Xmain()
  241. X{
  242. X  char buf[MAX_MESSAGE];
  243. X  int pos = 0; /* position in message buffer */
  244. X  char ch;
  245. X  int i;
  246. X
  247. X  puts("Welcome to mtalk!  Attempting connection...");
  248. X  init();
  249. X  login();
  250. X  buf[0] = '\0';
  251. X
  252. X  /* The program's main loop.  This could be accomplished more easily
  253. X     by forking another process, one to process user input and one
  254. X     to process messages from the server, but that would double the
  255. X     memory that this program uses. */
  256. X  for(;;) {
  257. X    /* First check for input from the terminal */
  258. X    if(read(0,&ch,1) > 0)
  259. X      switch(ch) {
  260. X        case 127 :
  261. X        case 8 : /* Erase */
  262. X                 if(pos > 0) {
  263. X                   pos--;
  264. X                   buf[pos] = '\0';
  265. X                   write(1,"\b \b",3);
  266. X                   }
  267. X                 break;
  268. X        case 3 : /* CTRL-C */
  269. X                 doexit();
  270. X        case 21 : /* Kill - CTRL-U */
  271. X                  pos = 0;
  272. X                  buf[0] = '\0';
  273. X                  write(1,"^U\r\n",4);
  274. X                  break;
  275. X        case 18 : /* CTRL-R - refresh line */
  276. X                  if(pos > 0) {
  277. X                    write(1,"^R\r\n",4);
  278. X                    write(1,buf,strlen(buf));
  279. X                    }
  280. X                  break;
  281. X        case 13 : write(1,"\r\n",2);
  282. X                  if(pos > 0) {
  283. X                    sendline(buf);
  284. X                    pos = 0;
  285. X                    buf[0] = '\0';
  286. X                    }
  287. X                  break;
  288. X        default : if(pos <= (MAX_MESSAGE - 2)) {
  289. X                    write(1,&ch,1);
  290. X                    buf[pos] = ch;
  291. X                    pos++;
  292. X                    buf[pos] = '\0';
  293. X                    }
  294. X                  break;
  295. X        }
  296. X
  297. X    /* Now check for incoming messages */
  298. X    if((i = msgrcv(ourmqid,&msgin,1024,0L,IPC_NOWAIT)) == -1)
  299. X      if(errno == EDOM) {
  300. X        perror("mtalk: msgrcv");
  301. X        cleanup();
  302. X        exit(1);
  303. X        }
  304. X    if(i >= 0) {
  305. X      if(msgin.msgtype == M_DMNTEXT) {
  306. X        write(1,msgin.contents,strlen(msgin.contents));
  307. X        write(1,"\r\n",2);
  308. X        continue;
  309. X        }
  310. X      if(msgin.msgtype == M_DMNFULL) {
  311. X        puts("Sorry, mtalk is full now.  Try again later.\r");
  312. X        cleanup();
  313. X        exit(0);
  314. X        }
  315. X      }
  316. X    }
  317. X}
  318. X
  319. X/* Initialize various things--message queues, signals, cbreak, etc. */
  320. Xinit()
  321. X{
  322. X  key_t ftok();
  323. X  int doexit();
  324. X
  325. X  /* First try to connect with the daemon's message queue...using the secret formula! */
  326. X  if((dmqid = msgget(ftok(DAEMON_PATH,1),0)) == -1) {
  327. X    perror("mtalk: msgget");
  328. X    exit(1);
  329. X    }
  330. X
  331. X  /* Now get our uid, and attempt to create our own message queue. */
  332. X  ouruid = getuid();
  333. X  if((ourmqid = msgget(ftok(CLIENT_PATH,ouruid),(0622 | IPC_CREAT))) == -1) {
  334. X    perror("mtalk: msgget");
  335. X    exit(1);
  336. X    }
  337. X
  338. X  /* Set up to handle signals - we DON'T want to exit without notifying
  339. X     the server, if at all possible. */
  340. X  signal(SIGHUP,doexit);
  341. X  signal(SIGQUIT,doexit);
  342. X  signal(SIGTERM,doexit);
  343. X  signal(SIGREST,doexit);
  344. X
  345. X  /* Now set up stdin */
  346. X  oldfcntl = fcntl(0,F_GETFL,0);
  347. X  fcntl(0,F_SETFL,(oldfcntl | O_NDELAY));
  348. X  gtty(0,&t0);
  349. X  t0.sg_flags |= RAW;
  350. X  t0.sg_flags &= ~ECHO;
  351. X  stty(0,&t0);
  352. X
  353. X  /* Set up stdout */
  354. X  gtty(1,&t1);
  355. X  t1.sg_flags |= RAW;
  356. X  stty(1,&t1);
  357. X}
  358. X
  359. X/* Send a login attempt to the daemon */
  360. Xlogin()
  361. X{
  362. X  struct {
  363. X    long mtype;
  364. X    struct usrin info;
  365. X    } mbuf;
  366. X
  367. X  mbuf.mtype = M_USRIN;
  368. X  mbuf.info.uid = ouruid;
  369. X  msgsnd(dmqid,&mbuf,sizeof(struct usrin),0);
  370. X}
  371. X
  372. X/* This function takes a line of text that the user has entered, and
  373. X   determines whether it is a public message or a command.  If it is
  374. X   a public message, it sends it; otherwise, it tries to process the
  375. X   command. */
  376. Xsendline(txt)
  377. Xchar *txt;
  378. X{
  379. X  struct {
  380. X    long mtype;
  381. X    struct usrmsg info;
  382. X    } mbuf;
  383. X
  384. X  if(txt[0] != '/') { /* Not a command */
  385. X    mbuf.mtype = M_USRMSG;
  386. X    mbuf.info.uid = ouruid;
  387. X    strcpy(mbuf.info.text,txt);
  388. X    msgsnd(dmqid,&mbuf,sizeof(struct usrmsg),0);
  389. X    return;
  390. X    }
  391. X
  392. X  /* Determine which command it is by letter following slash */
  393. X  switch(tolower(txt[1])) {
  394. X    case 'w' : /* Whisper */
  395. X               dowhisper(txt);
  396. X               return;
  397. X    case 'u' : /* Who (Users) */
  398. X               dowho();
  399. X               return;
  400. X    case 'e' : /* Exit */
  401. X               doexit();
  402. X    case '?' :
  403. X    case 'h' : /* Help */
  404. X               puts("Commands available\r");
  405. X               puts("  /users - show current mtalk users\r");
  406. X               puts("  /whisper name message - send message privately to user name\r");
  407. X               puts("  /exit - exit the program\r");
  408. X               puts("  /help - see this help screen\r");
  409. X               return;
  410. X    default : puts("Unknown command.\r");
  411. X              return;
  412. X    }
  413. X}
  414. X
  415. X/* Process the whisper command */
  416. Xdowhisper(txt)
  417. Xchar *txt;
  418. X{
  419. X  struct {
  420. X    long mtype;
  421. X    struct usrcmd info;
  422. X    } mbuf;
  423. X  char dummy[20];
  424. X  char *p;
  425. X
  426. X  mbuf.mtype = M_USRCMD;
  427. X  mbuf.info.uid = ouruid;
  428. X  mbuf.info.cmd = U_WHISPER;
  429. X
  430. X  /* Make sure there is a first space and hence a name in the line */
  431. X  if((p = (char *)index(txt,' ')) == NULL) {
  432. X    puts("usage: /whisper name text\r");
  433. X    return;
  434. X    }
  435. X
  436. X  /* Attempt to extract the name.  The line should be of the form
  437. X     /whisper name lots of text */
  438. X  sscanf(txt,"%s %s",dummy,mbuf.info.user);
  439. X
  440. X  /* Now obtain a pointer to the text of the message */
  441. X  /* We want to find the second space in the string. */
  442. X  p++;
  443. X  if((p = (char *)index(p,' ')) == NULL) {
  444. X    puts("usage: /whisper name text\r");
  445. X    return;
  446. X    }
  447. X  p++;
  448. X
  449. X  /* p now points to the message text.  Copy it into our structure and send it. */
  450. X  strcpy(mbuf.info.text,p);
  451. X  msgsnd(dmqid,&mbuf,sizeof(struct usrcmd),0);
  452. X}
  453. X
  454. X/* Process a who command--send a request for a list of users. */
  455. Xdowho()
  456. X{
  457. X  struct {
  458. X    long mtype;
  459. X    struct usrcmd info;
  460. X    } mbuf;
  461. X
  462. X  mbuf.mtype = M_USRCMD;
  463. X  mbuf.info.uid = ouruid;
  464. X  mbuf.info.cmd = U_WHO;
  465. X  msgsnd(dmqid,&mbuf,sizeof(struct usrcmd),0);
  466. X}
  467. X
  468. X/* Process an exit command--send an exit message, and exit the program.
  469. X   Also called by some signals. */
  470. Xdoexit()
  471. X{
  472. X  struct {
  473. X    long mtype;
  474. X    struct usrcmd info;
  475. X    } mbuf;
  476. X
  477. X  mbuf.mtype = M_USRCMD;
  478. X  mbuf.info.uid = ouruid;
  479. X  mbuf.info.cmd = U_EXIT;
  480. X  msgsnd(dmqid,&mbuf,sizeof(struct usrcmd),0);
  481. X
  482. X  write(1,"\r\n",2);
  483. X  cleanup();
  484. X
  485. X  exit(0);
  486. X}
  487. X
  488. X/* un-initialize various things--called before exiting. */
  489. Xcleanup()
  490. X{
  491. X  /* Kill our message queue */
  492. X  msgctl(ourmqid,IPC_RMID,(struct msqid_ds *)0);
  493. X
  494. X  /* Set terminal back to normal */
  495. X  fcntl(0,F_SETFL,oldfcntl);
  496. X  gtty(0,&t0);
  497. X  t0.sg_flags &= ~RAW;
  498. X  t0.sg_flags |= ECHO;
  499. X  stty(0,&t0);
  500. X  gtty(1,&t1);
  501. X  t1.sg_flags &= ~RAW;
  502. X  stty(1,&t1);
  503. X}
  504. END_OF_mtalk.c
  505. if test 7324 -ne `wc -c <mtalk.c`; then
  506.     echo shar: \"mtalk.c\" unpacked with wrong size!
  507. fi
  508. # end of overwriting check
  509. fi
  510. if test -f mtalk.h -a "${1}" != "-c" ; then 
  511.   echo shar: Will not over-write existing file \"mtalk.h\"
  512. else
  513. echo shar: Extracting \"mtalk.h\" \(1981 characters\)
  514. sed "s/^X//" >mtalk.h <<'END_OF_mtalk.h'
  515. X/* mtalk - general info for both daemon and client.
  516. X *
  517. X * Copyright (c) 1991, Matthew Kimmel.  Permission granted for unlimited
  518. X * non-commercial use and distribution.
  519. X */
  520. X
  521. X/* Number of users that can use mtalk at one time. */
  522. X#define MAX_USERS    3
  523. X
  524. X/* Maximum length of a user's message--must be smaller then the maximum
  525. X   size of a msg queue message.  In the tradition of MUDs, I have it set
  526. X   to 256 bytes.  Newline never included. */
  527. X#define MAX_MESSAGE    256
  528. X
  529. X/* Where the executables are located--used with ftok(). */
  530. X#define DAEMON_PATH    "/usr/kimmel/work/mtalk/mtalkd"
  531. X#define CLIENT_PATH    "/usr/kimmel/work/mtalk/mtalk"
  532. X
  533. X/* Message types that can be sent to the daemon */
  534. X#define M_USRIN        1L    /* User logging in. */
  535. X#define M_USRMSG    2L    /* A public message from a user */
  536. X#define M_USRCMD    3L    /* A command from a user */
  537. X
  538. X/* Message types that can be received by the client */
  539. X#define M_DMNTEXT    4L    /* Text to be displayed to user */
  540. X#define M_DMNFULL    5L    /* Too many users; user cannot join mtalk */
  541. X
  542. X/* User commands that can be sent to the server as type M_USRCMD */
  543. X#define U_WHISPER    0    /* Whisper to a specific user */
  544. X#define U_WHO        1    /* Who's on? */
  545. X#define U_EXIT        2    /* Exit mtalk */
  546. X
  547. X/* Structure passed with M_USRIN type */
  548. Xstruct usrin {
  549. X  int uid;    /* This user's uid */
  550. X  };
  551. X
  552. X/* Structure passed with M_USRMSG type */
  553. Xstruct usrmsg {
  554. X  int uid;    /* User's uid */
  555. X  char text[MAX_MESSAGE];     /* Text of message */
  556. X  };
  557. X
  558. X/* Structure passed with M_USRCMD type */
  559. Xstruct usrcmd {
  560. X  int uid;    /* User's uid */
  561. X  int cmd;    /* Which command is being sent */
  562. X  char user[31];    /* Which user it concerns */
  563. X  char text[MAX_MESSAGE];    /* Text that accompanies the command */
  564. X  };
  565. X
  566. X/* Note: type M_DMNTEXT is just straight text, and type M_DMNFULL has
  567. X   no parameters. */
  568. X
  569. X/* This is a "generic message" structure.  It is used to receive messages
  570. X   before their types (and thus formats) are known. */
  571. Xstruct genmsg {
  572. X  long msgtype;
  573. X  char contents[1024]; /* Just to be safe */
  574. X  };
  575. X
  576. END_OF_mtalk.h
  577. if test 1981 -ne `wc -c <mtalk.h`; then
  578.     echo shar: \"mtalk.h\" unpacked with wrong size!
  579. fi
  580. # end of overwriting check
  581. fi
  582. if test -f mtalk.man -a "${1}" != "-c" ; then 
  583.   echo shar: Will not over-write existing file \"mtalk.man\"
  584. else
  585. echo shar: Extracting \"mtalk.man\" \(1005 characters\)
  586. sed "s/^X//" >mtalk.man <<'END_OF_mtalk.man'
  587. X.TH mtalk
  588. X.SH USAGE
  589. X.PP
  590. Xmtalk
  591. X.SH SYNOPSIS
  592. X.PP
  593. Xmtalk is the user interface and client for
  594. Xthe mtalk multiuser talk program.  It allows the
  595. Xuser to log into mtalk and chat with other people
  596. Xusing mtalk.  It takes no parameters.
  597. X.SH COMMANDS
  598. X.PP
  599. XOnce in mtalk, several commands are available to the
  600. Xuser.  All commands have the slash character '/' as
  601. Xthe first character in the line; any line input that
  602. Xdoes not start with a slash is taken to be a public message
  603. Xto be sent to everyone logged onto mtalk.  The commands are:
  604. X.PP
  605. X/users - Shows all users currently logged on to mtalk.
  606. X.PP
  607. X/whisper <name> <message> - Send <message> privately to user <name>.
  608. X.PP
  609. X/exit - Exit the program.
  610. X.PP
  611. X/help - Get a help message describing commands.
  612. X.SH LINE EDITING
  613. X.PP
  614. XWhile entering a line of text, limited line editing is available.
  615. XBackspace or delete deletes the last character you typed.  CTRL-U
  616. Xcancels the line.  CTRL-R redisplays your line of text up to the last
  617. Xcharacter you typed.
  618. X.SH SEE ALSO
  619. X.PP
  620. Xmtalkd
  621. END_OF_mtalk.man
  622. if test 1005 -ne `wc -c <mtalk.man`; then
  623.     echo shar: \"mtalk.man\" unpacked with wrong size!
  624. fi
  625. # end of overwriting check
  626. fi
  627. if test -f mtalkd.c -a "${1}" != "-c" ; then 
  628.   echo shar: Will not over-write existing file \"mtalkd.c\"
  629. else
  630. echo shar: Extracting \"mtalkd.c\" \(6803 characters\)
  631. sed "s/^X//" >mtalkd.c <<'END_OF_mtalkd.c'
  632. X/* mtalkd - daemon for mtalk.  Handles and redistributes user messages
  633. X * and requests.
  634. X *
  635. X * Copyright (c) 1991, Matthew Kimmel.  Permission granted for unlimited
  636. X * non-commercial use and distribution.
  637. X */
  638. X#include <stdio.h>
  639. X#include <string.h>
  640. X#include <signal.h>
  641. X#include <pwd.h>
  642. X#include <sys/msg.h>
  643. X#include "mtalk.h"
  644. X
  645. Xstruct user {  /* Record of logged-on user */
  646. X  int uid;    /* if this is -1, this user slot is empty */
  647. X  int mqid;    /* user's message queue id */
  648. X  char name[31]; /* Their name */
  649. X  };
  650. X
  651. Xstruct genmsg msgin; /* Soon-to-be-malloc'd structure we use to catch incoming messages before processing */
  652. Xint ourmqid; /* Our message queue id; */
  653. Xstruct user users[MAX_USERS]; /* List of active users */
  654. X
  655. Xmain()
  656. X{
  657. X  init(); /* Initialize stuff */
  658. X  server(); /* Go be the mtalk server */
  659. X  cleanup(); /* If we get here, we'd better clean things up */
  660. X}
  661. X
  662. X/* Initialize various stuff--memory, message queue, signals, etc. */
  663. Xinit()
  664. X{
  665. X  key_t ftok();
  666. X  int handle_sigs();
  667. X  char *malloc();
  668. X  int i;
  669. X
  670. X  /* Get our incoming message queue using the....magic formula! */
  671. X  if((ourmqid = msgget(ftok(DAEMON_PATH,1),(0622 | IPC_CREAT))) == -1) {
  672. X    perror("mtalkd: msgget");
  673. X    exit(1);
  674. X    }
  675. X
  676. X  /* Set up to handle signals */
  677. X  signal(SIGHUP,SIG_IGN);
  678. X  signal(SIGINT,SIG_IGN);
  679. X  signal(SIGQUIT,SIG_IGN);
  680. X  signal(SIGTERM,handle_sigs);
  681. X  signal(SIGREST,handle_sigs);
  682. X
  683. X  /* Initialize user list to no users */
  684. X  for(i = 0;i < MAX_USERS;i++)
  685. X    users[i].uid = -1;
  686. X}
  687. X
  688. X/* This is the main loop of mtalkd.  It waits for messages, and
  689. X   processes them.  It should spend most of its time blocked when
  690. X   nothing is happening, and save system time. */
  691. Xserver()
  692. X{
  693. X  for(;;) {
  694. X    if(msgrcv(ourmqid,&msgin,1024,0L,0) == -1) { /* This should NEVER happen. */
  695. X      perror("mtalkd: msgrcv");
  696. X      exit(1);
  697. X      }
  698. X    if(msgin.msgtype == M_USRMSG) { /* A public message from a user */
  699. X      usrpubmsg(msgin.contents);
  700. X      continue;
  701. X      }
  702. X    if(msgin.msgtype == M_USRCMD) { /* A command from a user */
  703. X      usrcommand(msgin.contents);
  704. X      continue;
  705. X      }
  706. X    if(msgin.msgtype == M_USRIN) { /* A user logging in */
  707. X      usrlogin(msgin.contents);
  708. X      continue;
  709. X      }
  710. X    }
  711. X}
  712. X
  713. X/* Assemble and distribute a public message from a user */
  714. Xusrpubmsg(mesg)
  715. Xstruct usrmsg *mesg;
  716. X{
  717. X  char txt[315]; /* Better safe than sorry */
  718. X  int i;
  719. X
  720. X  for(i=0;i<MAX_USERS;i++)
  721. X    if(users[i].uid == mesg->uid)
  722. X      break;
  723. X
  724. X  if(i == MAX_USERS) { /* Unknow uid--this should NEVER happen */
  725. X    sprintf(txt,"Message from unlogged uid %d -- tell programmer!",mesg->uid);
  726. X    sendtoall(txt);
  727. X    }
  728. X
  729. X  sprintf(txt,"%s: %s",users[i].name,mesg->text);
  730. X  sendtoall(txt);
  731. X}
  732. X
  733. X/* Process an incoming command from a user */
  734. Xusrcommand(command)
  735. Xstruct usrcmd *command;
  736. X{
  737. X  switch(command->cmd) {
  738. X    case U_WHISPER : dowhisper(command->uid,command->user,command->text);
  739. X                     break;
  740. X    case U_WHO : dowho(command->uid);
  741. X                 break;
  742. X    case U_EXIT : doexit(command->uid);
  743. X                  break;
  744. X    } /* Unrecognized commands are ignored. */
  745. X}
  746. X
  747. X/* Process the whisper command */
  748. Xdowhisper(uid,usr,text)
  749. Xint uid;  /* uid of sending user */
  750. Xchar *usr; /* name of receiving user */
  751. Xchar *text; /* Text to be whispered */
  752. X{
  753. X  int duid = -1; /* Receipient's uid */
  754. X  char *sname; /* Pointer to sender's name */
  755. X  int i;
  756. X  char buf[1024];
  757. X
  758. X  for(i=0;i<MAX_USERS;i++) {
  759. X    if(users[i].uid == uid)
  760. X      sname = users[i].name;
  761. X    if(!strcmp(users[i].name,usr))
  762. X      duid = users[i].uid;
  763. X      }
  764. X
  765. X  if(duid == -1) { /* User not found */
  766. X    sprintf(buf,"%s: no such user",usr);
  767. X    sendtouid(uid,buf);
  768. X    }
  769. X
  770. X  /* Assemble whisper and send it */
  771. X  sprintf(buf,"%s whispers: %s",sname,text);
  772. X  sendtouid(duid,buf);
  773. X}
  774. X
  775. X/* Process the who command */
  776. Xdowho(uid)
  777. Xint uid; /* uid of requestor */
  778. X{
  779. X  char buf[1024];
  780. X  int i;
  781. X
  782. X  /* Assemble a string to be sent to the user.  This may break if */
  783. X  /* MAX_USERS is more than 30. */
  784. X  strcpy(buf,"Users currently logged on:\r\n");
  785. X  for(i=0;i<MAX_USERS;i++)
  786. X    if(users[i].uid != -1) {
  787. X      strcat(buf,"  ");
  788. X      strcat(buf,users[i].name);
  789. X      strcat(buf,"\r\n");
  790. X      }
  791. X
  792. X  /* Send the string */
  793. X  sendtouid(uid,buf);
  794. X}
  795. X
  796. X/* Process the exit command */
  797. Xdoexit(uid)
  798. Xint uid; /* uid of exiter */
  799. X{
  800. X  int i;
  801. X  char buf[1024];
  802. X
  803. X  /* Find user and delete him from user list, and assemble a string
  804. X     notifying other users that he has exited. */
  805. X  for(i=0;i<MAX_USERS;i++)
  806. X    if(users[i].uid == uid) {
  807. X      sprintf(buf,"%s has exited.",users[i].name);
  808. X      users[i].uid = -1;
  809. X      }
  810. X
  811. X  /* Send the notification to everyone else. */
  812. X  sendtoall(buf);
  813. X}
  814. X/* Log a user into the system (if there's a free slot) */
  815. Xusrlogin(usr)
  816. Xstruct usrin *usr;
  817. X{
  818. X  key_t ftok();
  819. X  int slot, tmpid;
  820. X  struct passwd *pw;
  821. X  struct {
  822. X    long mtype;
  823. X    } fullmsg;
  824. X  char announce[50];
  825. X
  826. X  /* First, try to get their message queue */
  827. X  if((tmpid = msgget(ftok(CLIENT_PATH,usr->uid),0)) == -1) {
  828. X    perror("mtalkd: msgget");
  829. X    return;
  830. X    }
  831. X
  832. X  /* Look for a slot for the user. */
  833. X  for(slot=0;slot<MAX_USERS;slot++)
  834. X    if(users[slot].uid == -1)
  835. X      break;
  836. X
  837. X  /* Send a full message if no slots open */
  838. X  if(slot == MAX_USERS) {
  839. X    fullmsg.mtype = M_DMNFULL;
  840. X    msgsnd(tmpid,&fullmsg,0,0);
  841. X    return;
  842. X    }
  843. X
  844. X  /* Otherwise, put the user in the user records. */
  845. X  users[slot].uid = usr->uid;
  846. X  users[slot].mqid = tmpid;
  847. X  setpwent();
  848. X  pw = getpwuid(usr->uid);
  849. X  strncpy(users[slot].name,pw->pw_name,30);
  850. X  endpwent();
  851. X
  852. X  /* Announce that the user has logged in */
  853. X  sprintf(announce,"%s has logged in.",users[slot].name);
  854. X  sendtoall(announce);
  855. X}
  856. X
  857. X/* Send a line of text to all users logged in */
  858. Xsendtoall(txt)
  859. Xchar *txt;
  860. X{
  861. X  int i;
  862. X  struct {
  863. X    long mtype;
  864. X    char text[1024];
  865. X    } mbuf;
  866. X
  867. X  /* Now do a msgsnd to all users */
  868. X  mbuf.mtype = M_DMNTEXT;
  869. X  strncpy(mbuf.text,txt,1023);
  870. X  for(i=0;i<MAX_USERS;i++)
  871. X    if(users[i].uid != -1)
  872. X      msgsnd(users[i].mqid,&mbuf,(strlen(mbuf.text)+1),0);
  873. X}
  874. X
  875. X/* Send a line of text to a specific uid.  If the uid is not found in the
  876. X   list of users, the message is discarded, to save hassle. */
  877. Xsendtouid(uid,txt)
  878. Xint uid;
  879. Xchar *txt;
  880. X{
  881. X  int i;
  882. X  struct {
  883. X    long mtype;
  884. X    char text[1024];
  885. X    } mbuf;
  886. X
  887. X  /* Find the uid, and send the message to that user */
  888. X  for(i=0;i<MAX_USERS;i++)
  889. X    if(users[i].uid == uid) {
  890. X      mbuf.mtype = M_DMNTEXT;
  891. X      strncpy(mbuf.text,txt,1023);
  892. X      msgsnd(users[i].mqid,&mbuf,(strlen(mbuf.text)+1),0);
  893. X      }
  894. X}
  895. X
  896. X/* Handle any caught signals--at present, all caught signals make the
  897. X   program clean up and terminate. */
  898. Xhandle_sigs()
  899. X{
  900. X  cleanup();
  901. X  exit(0);
  902. X}
  903. X
  904. X/* This function kills our message queue and frees memory when we exit.
  905. X   It leaves any users stranded, since this should never happen when
  906. X   users are logged on! */
  907. Xcleanup()
  908. X{
  909. X  msgctl(ourmqid,IPC_RMID,NULL);
  910. X}
  911. X
  912. END_OF_mtalkd.c
  913. if test 6803 -ne `wc -c <mtalkd.c`; then
  914.     echo shar: \"mtalkd.c\" unpacked with wrong size!
  915. fi
  916. # end of overwriting check
  917. fi
  918. if test -f mtalkd.man -a "${1}" != "-c" ; then 
  919.   echo shar: Will not over-write existing file \"mtalkd.man\"
  920. else
  921. echo shar: Extracting \"mtalkd.man\" \(310 characters\)
  922. sed "s/^X//" >mtalkd.man <<'END_OF_mtalkd.man'
  923. X.TH mtalkd
  924. X.SH USAGE
  925. X.PP 
  926. Xmtalkd&
  927. X.SH SYNOPSIS
  928. X.PP 
  929. Xmtalkd is the daemon for mtalk.
  930. XIt must be running in memory before
  931. Xmtalk is invoked by any user.  It
  932. Xrequires that the msg device driver be
  933. Xinstalled before it is invoked.  It
  934. Xtakes no parameters.
  935. X.SH FILES
  936. X.PP
  937. Xmtalk, /drv/msg
  938. X.SH SEE ALSO
  939. X.PP
  940. Xmtalk, drvld
  941. END_OF_mtalkd.man
  942. if test 310 -ne `wc -c <mtalkd.man`; then
  943.     echo shar: \"mtalkd.man\" unpacked with wrong size!
  944. fi
  945. # end of overwriting check
  946. fi
  947. echo shar: End of shell archive.
  948. exit 0
  949.  
  950. exit 0 # Just in case...
  951. -- 
  952. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  953. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  954. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  955. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  956.